#include "globals.h"
#include "errno.h"

#include "util/string.h"
#include "util/debug.h"

#include "mm/mmobj.h"
#include "mm/pframe.h"
#include "mm/mm.h"
#include "mm/page.h"
#include "mm/slab.h"
#include "mm/tlb.h"

#include "vm/vmmap.h"
#include "vm/shadow.h"
#include "vm/shadowd.h"

#define SHADOW_SINGLETON_THRESHOLD 5

int shadow_count = 0; /* for debugging/verification purposes */
#ifdef __SHADOWD__
/*
 * number of shadow objects with a single parent, that is another shadow
 * object in the shadow objects tree(singletons)
 */
static int shadow_singleton_count = 0;
#endif

static slab_allocator_t *shadow_allocator;

static void shadow_ref(mmobj_t *o);
static void shadow_put(mmobj_t *o);
static int  shadow_lookuppage(mmobj_t *o, uint32_t pagenum, int forwrite, pframe_t **pf);
static int  shadow_fillpage(mmobj_t *o, pframe_t *pf);
static int  shadow_dirtypage(mmobj_t *o, pframe_t *pf);
static int  shadow_cleanpage(mmobj_t *o, pframe_t *pf);

static mmobj_ops_t shadow_mmobj_ops = {
        .ref = shadow_ref,
        .put = shadow_put,
        .lookuppage = shadow_lookuppage,
        .fillpage  = shadow_fillpage,
        .dirtypage = shadow_dirtypage,
        .cleanpage = shadow_cleanpage
};

/*
 * This function is called at boot time to initialize the
 * shadow page sub system. Currently it only initializes the
 * shadow_allocator object.
 */
void
shadow_init()
{
        /*NOT_YET_IMPLEMENTED("VM: shadow_init");*/
        shadow_allocator = slab_allocator_create("shadow", sizeof(mmobj_t));
        KASSERT(shadow_allocator);
}

/*
 * You'll want to use the shadow_allocator to allocate the mmobj to
 * return, then then initialize it. Take a look in mm/mmobj.h for
 * macros which can be of use here. Make sure your initial
 * reference count is correct.
 */
mmobj_t *
shadow_create()
{
/*        NOT_YET_IMPLEMENTED("VM: shadow_create");*/


        mmobj_t *newMmobj = NULL;
        newMmobj=(mmobj_t*)slab_obj_alloc(shadow_allocator);
        if(newMmobj==NULL)
        {
                dbg_print("allocate space for shadow object failed here");
                return newMmobj;
        }
          newMmobj->mmo_ops = &shadow_mmobj_ops;
          newMmobj->mmo_refcount = 1;
          newMmobj->mmo_nrespages = 0; 
          newMmobj->mmo_un.mmo_bottom_obj = NULL;
          newMmobj->mmo_shadowed = NULL; /*?? should set to the object we shadow*/
/*        mmobj_init(newMmobj, &shadow_mmobj_ops);*/
/*        newMmobj->mmo_shadowed = 
        newMmobj->mmo_un.mmo_bottom_obj*/
        return newMmobj;
}

/* Implementation of mmobj entry points: */

/*
 * Increment the reference count on the object.
 */
static void
shadow_ref(mmobj_t *o)
{
/*        NOT_YET_IMPLEMENTED("VM: shadow_ref");*/
        KASSERT(o && (0 < o->mmo_refcount) && (&shadow_mmobj_ops == o->mmo_ops));
        o->mmo_refcount++;
}

/*
 * Decrement the reference count on the object. If, however, the
 * reference count on the object reaches the number of resident
 * pages of the object, we can conclude that the object is no
 * longer in use and, since it is a shadow object, it will never
 * be used again. You should unpin and uncache all of the object's
 * pages and then free the object itself.
 */
static void
shadow_put(mmobj_t *o)
{
/*        NOT_YET_IMPLEMENTED("VM: shadow_put");*/
        KASSERT(o && (0 < o->mmo_refcount) && (&shadow_mmobj_ops == o->mmo_ops));
        o->mmo_refcount--;
        if(o->mmo_nrespages==o->mmo_refcount)
        {
                pframe_t *pf = NULL;
               list_iterate_begin(&o->mmo_respages, pf, pframe_t, pf_olink) { 
                                        /* how to uncache?*/
                            pframe_unpin(pf);   /*we dont need to unpin the pframe manually*/
                            list_remove(&pf->pf_olink);
                            list_remove(&pf->pf_hlink);
                }list_iterate_end();
                slab_obj_free(shadow_allocator,o); /*free itself*/
        }
}

/* This function looks up the given page in this shadow object. The
 * forwrite argument is true if the page is being looked up for
 * writing, false if it is being looked up for reading. This function
 * must handle all do-not-copy-on-not-write magic (i.e. when forwrite
 * is false find the first shadow object in the chain which has the
 * given page resident). copy-on-write magic (necessary when forwrite
 * is true) is handled in shadow_fillpage, not here. */
static int
shadow_lookuppage(mmobj_t *o, uint32_t pagenum, int forwrite, pframe_t **pf)
{
 /*   pframe_t *pframe_get_resident(struct mmobj *o, uint32_t pagenum);

int pframe_get(struct mmobj *o, uint32_t pagenum, pframe_t **result);
int pframe_lookup(struct mmobj *o, uint32_t pagenum, int forwrite, pframe_t **result);
void pframe_migrate(pframe_t *pf, mmobj_t *dest);*/

/*        NOT_YET_IMPLEMENTED("VM: shadow_lookuppage");*/
    mmobj_t* cur_obj = o;
    pframe_t* ppf = pframe_get_resident(cur_obj, pagenum);

    while(ppf == NULL && cur_obj != mmobj_bottom_obj(o)/* cur_obj->mmo_shadowed == NULL*/)
    {
        cur_obj = cur_obj->mmo_shadowed;
         ppf = pframe_get_resident(cur_obj, pagenum);
    }

    if(ppf != NULL)
    {
        *pf = ppf;
        return 0;
    }
        

    return pframe_get(cur_obj, pagenum, pf);   

/*
                   if(forwrite)
                   {
                       /* pframe_t* */
                      /*  pframe_t* ppf = pframe_get_resident(o, pagenum);
                      

                        mmobj_t * bottom = mmobj_bottom_obj(o);
                        pframe_t* new_pf;
                        return pframe_get(bottom, pagenum, pf);


                   }
                   do
                   {
                        if(pframe_get(o, pagenum, pf)==0)
                                return 0;
                        o = o->mmo_shadowed;              
                   }while(o);*/
       /* return -1;*/   /*-errno does not work*/
}

/* As per the specification in mmobj.h, fill the page frame starting
 * at address pf->pf_addr with the contents of the page identified by
 * pf->pf_obj and pf->pf_pagenum. This function handles all
 * copy-on-write magic (i.e. if there is a shadow object which has
 * data for the pf->pf_pagenum-th page then we should take that data,
 * if no such shadow object exists we need to follow the chain of
 * shadow objects all the way to the bottom object and take the data
 * for the pf->pf_pagenum-th page from the last object in the chain). */
static int
shadow_fillpage(mmobj_t *o, pframe_t *pf)
{
/*        NOT_YET_IMPLEMENTED("VM: shadow_fillpage");*/
        KASSERT(pframe_is_busy(pf));
        KASSERT(!pframe_is_pinned(pf));
        /*in sequential order*/


    mmobj_t* cur_obj = o;
    int pagenum = pf->pf_pagenum;

    pframe_t* ppf = pframe_get_resident(cur_obj, pagenum);

    while(ppf == NULL && cur_obj != mmobj_bottom_obj(o))
    {
        cur_obj = cur_obj->mmo_shadowed;
         ppf = pframe_get_resident(cur_obj, pagenum);
    }

    if(ppf = NULL)
    {
        pframe_get(cur_obj, pagenum, ppf); 
    }

    /*fill in the page*/
    memcpy(pf->pf_addr, ppf->pf_addr, PAGE_SIZE);


 /*       pframe_t * new_pframe;
                do
                {
                        if(pframe_get(o, pf->pf_pagenum, &new_pframe)==0)
                        {
                                memcpy(pf,new_pframe,sizeof(pframe_t));  
                                return 0;
                        }
                        o = o->mmo_shadowed;              
                }while(o);
        return -1;   */
}

/* These next two functions are not difficult. */

static int
shadow_dirtypage(mmobj_t *o, pframe_t *pf)
{
/*        NOT_YET_IMPLEMENTED("VM: shadow_dirtypage");*/
        /*pframe_t * new_pframe;
        if(pframe_get(o, pf->pf_pagenum, &new_pframe)==0)
                        {
                                memcpy(pf,new_pframe,sizeof(pframe_t));  
                                return 0;
                        }
        return -1;*/
        pframe_dirty(pf);
}

static int
shadow_cleanpage(mmobj_t *o, pframe_t *pf)
{
        /*        NOT_YET_IMPLEMENTED("VM: shadow_cleanpage");*/
      /*  pframe_t * new_pframe;
        if(pframe_get(o, pf->pf_pagenum, &new_pframe)==0)
                        {
                                memcpy(new_pframe,pf,sizeof(pframe_t)); 
                                return 0;
                        }
        return -1;*/
    pframe_clean(pf);
}
